using System;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Input;

using LightningTools.BCSTesterMan.Common.ExtensionMethods;
using LightningTools.BCSTesterMan.Common.Infrastructure;
using LightningTools.BCSTesterMan.Model;
using LightningTools.BCSTesterMan.Model.DataAccessLayer;

namespace LightningTools.BCSTesterMan.ViewModel
{
    public class MainWindowViewModel : ObservableObject
    {

        private readonly BcsHelper bcsHelper;
        private Entity activeEntity;
        private Method activeMethod;
        private Error currentError;
        private ObservableCollection<ErrorButton> errorButtons;
        private ICommand errorSearchCommand;
        private ObservableCollectionEx<EntityViewModel> externalContentTypes;
        private bool isEntitySelected;
        private bool isError;
        private bool isInvokingMethod;
        private bool isLoadingEntities;
        private bool isMethodSelected;
        private object selectedItem;
        private string url;

        /// <summary>
        /// Initializes a new instance of the <see cref="MainWindowViewModel"/> class.
        /// </summary>
        public MainWindowViewModel()
        {
            LoadErrorSearchButtons();
            LoadSettings();
            bcsHelper = BcsHelper.Instance;
            bcsHelper.RowLimit = 500;
        }

        /// <summary>
        /// Gets or sets the active method.
        /// </summary>
        /// <value>The active method.</value>
        public Method ActiveMethod
        {
            get { return activeMethod; }
            set
            {
                activeMethod = value;
                if (activeMethod != null)
                {
                    if (activeMethod.Parameters == null)
                    {
                        activeMethod.IsLoading = true;
                        activeMethod.Parameters = BcsHelper.GetInputParameters(activeMethod, url).ToObservableCollection();
                        activeMethod.IsLoading = false;
                    }

                    IsMethodSelected = true;
                    RaisePropertyChanged("ActiveMethod");
                }
                else
                {
                    IsMethodSelected = false;
                }
            }
        }

        /// <summary>
        /// Gets or sets the active entity.
        /// </summary>
        /// <value>The active entity.</value>
        public Entity ActiveEntity
        {
            get { return activeEntity; }
            set
            {
                activeEntity = value;

                IsEntitySelected = activeEntity != null;

                RaisePropertyChanged("ActiveEntity");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this a method is selected.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if a method is selected; otherwise, <c>false</c>.
        /// </value>
        public bool IsMethodSelected
        {
            get { return isMethodSelected; }
            set
            {
                isMethodSelected = value;
                RaisePropertyChanged("IsMethodSelected");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether an entity is selected.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if an entity is selected; otherwise, <c>false</c>.
        /// </value>
        public bool IsEntitySelected
        {
            get { return isEntitySelected; }
            set
            {
                isEntitySelected = value;
                RaisePropertyChanged("IsEntitySelected");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether there is currently an error.
        /// </summary>
        /// <value><c>true</c> if there is currently an error; otherwise, <c>false</c>.</value>
        public bool IsError
        {
            get { return isError; }
            set
            {
                isError = value;

                if (isError)
                {
                    ResetSelections();
                }

                RaisePropertyChanged("IsError");
            }
        }

        /// <summary>
        /// Gets the load External Content Type command.
        /// </summary>
        /// <value>The load External Content Type command.</value>
        public ICommand LoadEctCommand
        {
            get { return new RelayCommand(LoadExternalContentTypes, CanLoadExternalContentTypes); }
        }

        /// <summary>
        /// Gets the invoke method command.
        /// </summary>
        /// <value>The invoke method command.</value>
        public ICommand InvokeMethodCommand
        {
            get { return new RelayCommand(InvokeMethod, CanInvokeMethod); }
        }

        /// <summary>
        /// Gets the error search command.
        /// </summary>
        /// <value>The error search command.</value>
        public ICommand ErrorSearchCommand
        {
            get
            {
                if (CurrentError != null)
                {
                    return errorSearchCommand ??
                        (errorSearchCommand = new RelayCommand<ErrorButton>(e => LoadError(e.QueryString, CurrentError)));
                }
                return null;
            }
        }

        /// <summary>
        /// Gets or sets the URL for the BCS.
        /// </summary>
        /// <value>The URL.</value>
        public string Url
        {
            get { return url; }
            set
            {
                url = value;
                RaisePropertyChanged("Url");
            }
        }

        /// <summary>
        /// Gets or sets the selected item.
        /// </summary>
        /// <value>The selected item.</value>
        public object SelectedItem
        {
            get { return selectedItem; }
            set
            {
                selectedItem = value;
                RecordSelectedItem(value);
            }
        }

        /// <summary>
        /// Gets or sets the external content types.
        /// </summary>
        /// <value>The external content types.</value>
        public ObservableCollectionEx<EntityViewModel> ExternalContentTypes
        {
            get { return externalContentTypes; }
            set
            {
                externalContentTypes = value;
                RaisePropertyChanged("ExternalContentTypes");
            }
        }

        /// <summary>
        /// Gets the is entity tree view visible.
        /// </summary>
        /// <value>The is entity tree view visible.</value>
        public Visibility IsEntityTreeViewVisible
        {
            get
            {
                if (ExternalContentTypes != null)
                {
                    return ExternalContentTypes.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
                }

                return Visibility.Collapsed;
            }
        }

        /// <summary>
        /// Gets or sets the current error.
        /// </summary>
        /// <value>The current error.</value>
        public Error CurrentError
        {
            get { return currentError; }
            set
            {
                currentError = value;

                IsError = currentError != null;

                RaisePropertyChanged("CurrentError");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is loading entities.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is loading entities; otherwise, <c>false</c>.
        /// </value>
        public bool IsLoadingEntities
        {
            get { return isLoadingEntities; }
            set
            {
                isLoadingEntities = value;
                RaisePropertyChanged("IsLoadingEntities");
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is invoking a method.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is invoking a method; otherwise, <c>false</c>.
        /// </value>
        public bool IsInvokingMethod
        {
            get { return isInvokingMethod; }
            set
            {
                isInvokingMethod = value;
                RaisePropertyChanged("IsInvokingMethod");
            }
        }

        /// <summary>
        /// Gets or sets the error buttons.
        /// </summary>
        /// <value>The error buttons.</value>
        public ObservableCollection<ErrorButton> ErrorButtons
        {
            get { return errorButtons; }
            set
            {
                errorButtons = value;
                RaisePropertyChanged("ErrorButtons");
            }
        }

        /// <summary>
        /// Saves the settings.
        /// </summary>
        private void SaveSettings()
        {
            Configuration oConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

            oConfig.AppSettings.Settings["LastUsedUrl"].Value = Url;
            oConfig.Save(ConfigurationSaveMode.Modified);
            ConfigurationManager.RefreshSection("appSettings");
        }

        /// <summary>
        /// Loads the settings.
        /// </summary>
        private void LoadSettings()
        {
            try
            {
                Configuration oConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

                KeyValueConfigurationElement lastusedUrl = oConfig.AppSettings.Settings["LastUsedUrl"];
                if (lastusedUrl != null)
                {
                    Url = lastusedUrl.Value;
                }
                else
                {
                    oConfig.AppSettings.Settings.Add("LastUsedUrl", Url);
                    oConfig.Save(ConfigurationSaveMode.Modified);
                    ConfigurationManager.RefreshSection("appSettings");
                }
            }
            catch (Exception ex)
            {
                CurrentError = new Error(ex);
            }
        }

        /// <summary>
        /// Resets the selections.
        /// </summary>
        private void ResetSelections()
        {
            IsEntitySelected = false;
            IsMethodSelected = false;
        }

        /// <summary>
        /// Loads the error.
        /// </summary>
        /// <param name="command">The command.</param>
        /// <param name="error">The error.</param>
        private static void LoadError(string command, Error error)
        {
            if (error == null)
            {
                return;
            }

            if (command == "CopyToClipboard")
            {
                Clipboard.SetData(DataFormats.Text, error.Message);
            }
            else
            {
                string url = string.Format(command, error.Message);
                Process.Start(url);
            }
        }

        /// <summary>
        /// Determines whether this instance can invoke method.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance can invoke method; otherwise, <c>false</c>.
        /// </returns>
        private bool CanInvokeMethod()
        {
            return !IsInvokingMethod && ActiveMethod != null;
        }

        /// <summary>
        /// Invokes the method.
        /// </summary>
        private void InvokeMethod()
        {
            var t = new Thread(ThreadedInvokeMethod);
            t.Start();
        }

        /// <summary>
        /// Invokes the method on a new thread.
        /// </summary>
        private void ThreadedInvokeMethod()
        {
            try
            {
                IsInvokingMethod = true;
                ActiveMethod.MethodResponseDetail = bcsHelper.InvokeMethod(ActiveMethod);
            }
            catch (Exception ex)
            {
                CurrentError = new Error(ex);
            }
            finally
            {
                IsInvokingMethod = false;
            }
        }

        /// <summary>
        /// Loads the external content types.
        /// </summary>
        void LoadExternalContentTypes()
        {
            var thread = new Thread(ThreadedLoadExternalContentTypes);
            thread.Start();
        }

        /// <summary>
        /// Loads external content types on a new thread.
        /// </summary>
        private void ThreadedLoadExternalContentTypes()
        {
            try
            {
                IsLoadingEntities = true;
                ExternalContentTypes = new ObservableCollectionEx<EntityViewModel>();

                foreach (Entity entity in bcsHelper.GetEntities())
                {
                    ExternalContentTypes.Add(new EntityViewModel(entity));
                }

                SaveSettings();
            }
            catch (Exception ex)
            {
                CurrentError = new Error(ex);
            }
            finally
            {
                IsLoadingEntities = false;
            }
        }

        /// <summary>
        /// Determines whether this instance can load external content types.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance [can load external content types]; otherwise, <c>false</c>.
        /// </returns>
        bool CanLoadExternalContentTypes()
        {
            bool isValid = true;

            if (string.IsNullOrEmpty(Url) || IsLoadingEntities)
            {
                isValid = false;
            }

            if (isValid)
            {
                Uri uri;

                try
                {
                    uri = new Uri(Url);
                    Url = uri.ToString();
                    bcsHelper.Url = Url;
                }
                catch
                {
                    // Ignore if cannot convert to URI
                }
            }

            return isValid;
        }

        /// <summary>
        /// Records the selected item.
        /// </summary>
        /// <param name="value">The value.</param>
        private void RecordSelectedItem(object value)
        {
            ActiveMethod = null;
            ActiveEntity = null;

            try
            {
                object item = value;

                if (item is MethodViewModel)
                {
                    // Set Active item
                    ActiveMethod = ((MethodViewModel)item).Method;
                }
                else if (item is EntityViewModel)
                {
                    // Set Entity item
                    ActiveEntity = ((EntityViewModel)item).Entity;
                }
            }
            catch (Exception ex)
            {
                // Set current error
                CurrentError = new Error(ex);
            }
            finally
            {
                // 
                CurrentError = null;
            }
        }

        private void LoadErrorSearchButtons()
        {
            ErrorButtons = new ErrorButtonList();
        }
    }
}